#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>


int mousefd = -1;

void sig_fun() {
    int data = 0;
    int n = read(mousefd, &data, sizeof(data));
    if (n < 0) {
        printf("read mouse error\n");
    }
    else {
        printf("%d\n", data);
    }
}

/**
 * BSD异步IO示例
 * 异步IO读取鼠标数据: 鼠标驱动产生鼠标数据时, 发送SIGIO信号给进程, 通知读取数据
 * 配置异步IO步骤:
 * 1. 调用signal/sigaction为SIGIO建立信号处理程序
 * 2. 调用fcntl SETOWN设置进程ID/进程组ID, 用于告诉驱动/内核接收SIGIO信号
 * 3. 调用fcntl SETFL添加O_ASYN标志, 使得在该描述符上可以进行异步IO
 */
int main () {
    int ret;
    int mousepos;

    mousefd = open("/dev/input/mouse0", O_RDONLY);
    if (mousefd < 0) {
        perror("open mouse error");
        exit(0);
    }

    struct sigaction sa;
    struct sigaction od_sa;

    sa.sa_handler = sig_fun;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGIO); // SIGIO添加进信号集
    sa.sa_flags = 0;

    // 1. 调用sigaction为SIGIO信号建立信号处理程序
    sigaction(SIGIO, &sa, &od_sa); // 捕获SIGIO后, 处理信号时, 阻塞信号; 处理完毕后恢复

    // 2. 以命令F_SETOWN调用fcntl来设置进程ID, 用于接收对该描述符的信号(SIGIO)
    if (fcntl(mousefd, F_SETOWN, getpid()) < 0) {
        perror("fcntl F_SETOWN error");
        exit(1);
    }

    // 3. 以命令F_SETFL调用fcntl, 设置O_ASYNC文件状态标识, 使得在该描述符上可以进行异步IO
    int flag = fcntl(mousefd, F_GETFL);
    if (flag < 0) {
        perror("fcntl F_GETFL error");
        exit(1);
    }
    flag |= O_ASYNC;
    
    ret = fcntl(mousefd, F_SETFL, flag);
    if (ret < 0) {
        perror("fcntl F_SETFL error");
        exit(1);
    }
    
    while (1) {
        usleep(50);
    }

    sigaction(SIGIO, &od_sa, NULL); // 恢复signal mask

    close(mousefd); // 不可达
    return 0;
}
